/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.stdlib;

import java.math.BigInteger;
import java.util.Formatter;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Custom;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;
import fr.x9c.cadmium.util.Misc;

/**
 * Implements all primitives from 'ints.c'.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Ints {

    /**
     * No instance of this class.
     */
    private Ints() {
    } // end empty constructor

    /**
     * Parses a digit, that is: converts a character into its integer value in
     * a given base.
     * @param ch character to parse/convert
     * @param base base for conversion
     * @return the integer value of <tt>ch</tt>
     * @throws Fail.Exception if a parsing error occurs
     */
    private static int parseDigit(final char ch, final int base)
        throws Fail.Exception {
        final int res;
        if ((ch >= '0') && (ch <= '9')) {
            res = ch - '0';
        } else if ((ch >= 'A') && (ch <= 'F')) {
            res = ch - 'A' + 10;
        } else if ((ch >= 'a') && (ch <= 'f')) {
            res = ch - 'a' + 10;
        } else {
            res = -1;
        } // end if/elsif/else
        if ((res < 0) || (res >= base)) {
            Fail.failWith("int_of_string");
            return 0; // never reached
        } else {
            return res;
        } // end if/else
    } // end method 'parseDigit(char, int)'

    /**
     * Parses a string into a long.
     * @param s string to parse - should not be <tt>null</tt>
     * @param bits size of long value - should be > 0
     * @return <i>s</i> as a long
     * @throws Fail.Exception if s is not a valid long value
     * @throws Fail.Exception if the value to be returned is too large
     */
    private static long parseLong(final String s, final int bits)
        throws Fail.Exception {
        assert s != null : "null s";
        assert bits > 0 : "bits should be > 0";
        int idx = 0;
        int sign = 1;
        final int len  = s.length();
        BigInteger base = BigInteger.TEN;

        // determine sign
        if (s.charAt(idx) == '-') {
            idx++;
            sign = -1;
        } // end if

        // determine base
        if ((idx + 1 < len) && (s.charAt(idx) == '0')) {
            switch (s.charAt(idx + 1)) {
            case 'x':
            case 'X':
                base = BigInteger.valueOf(16);
                idx += 2;
                break;
            case 'o':
            case 'O':
                base = BigInteger.valueOf(8);
                idx += 2;
                break;
            case 'b':
            case 'B':
                base = BigInteger.valueOf(2);
                idx += 2;
                break;
            default:
            } // end switch
        } // end if

        // actual parse
        final int b = base.intValue();
        if (idx >= len) {
            Fail.failWith("int_of_string");
            return 0; // never reached
        } // end if
        BigInteger res = BigInteger.valueOf(parseDigit(s.charAt(idx++), b));
        for (; idx < len; idx++) {
            final char ch = s.charAt(idx);
            if (ch == '_') {
                continue;
            } // end if
            res = res.multiply(base).add(BigInteger.valueOf(parseDigit(ch, b)));
        } // end for

        // test that result fits the given size
        final BigInteger min;
        final BigInteger max;
        if (b == 10) {
            min = BigInteger.valueOf(2).pow(bits - 1).negate();
            max = BigInteger.valueOf(2).pow(bits - 1).subtract(BigInteger.ONE);
        } else {
            min = BigInteger.valueOf(2).pow(bits).subtract(BigInteger.ONE).negate();
            max = BigInteger.valueOf(2).pow(bits).subtract(BigInteger.ONE);
        } // end if/else
        if ((res.compareTo(min) >= 0) && (res.compareTo(max) <= 0)) {
            return sign < 0 ? -res.longValue() : res.longValue();
        } else {
            Fail.failWith("int_of_string");
            return 0; // never reached
        } // end if/else
    } // end method 'parseLong(String, int)'

    /**
     * Compares two int values.
     * @param ctxt context
     * @param v1 first int value
     * @param v2 second int value
     * @return <tt>-1</tt>, <tt>0</tt>, <tt>1</tt> whether the first value is
     *         less than, equal to or greater than the second one
     */
    @Primitive
    public static Value caml_int_compare(final CodeRunner ctxt,
                                         final Value v1,
                                         final Value v2) {
        final int r1 = v1.asLong();
        final int r2 = v2.asLong();
        final int res;
        if (r1 < r2) {
            return Compare.LESS_VALUE;
        } else if (r1 > r2) {
            return Compare.GREATER_VALUE;
        } else {
            return Compare.EQUAL_VALUE;
        } // end if/elsif/else
    } // end method 'caml_int_compare(CodeRunner, Value, Value)'

    /**
     * Converts a string value into an int value.
     * @param ctxt context
     * @param s string to convert
     * @return the int value corresponding to <tt>s</tt>
     * @throws Fail.Exception if a parsing error occurs
     */
    @Primitive
    public static Value caml_int_of_string(final CodeRunner ctxt,
                                           final Value s)
        throws Fail.Exception {
        return Value.createFromLong((int) parseLong(s.asBlock().asString(), 31));
    } // end method 'caml_int_of_string(CodeRunner, Value)'

    /**
     * Formats an integer, that is converts it to a string.
     * @param ctxt context
     * @param fmt conversion format
     * @param arg integer to convert
     * @return <tt>arg</tt> as a string
     */
    @Primitive
    public static Value caml_format_int(final CodeRunner ctxt,
                                        final Value fmt,
                                        final Value arg) {
        final StringBuilder sb = new StringBuilder();
        final Formatter f = new Formatter(sb);
        String format = fmt.asBlock().asString();
        int argument = arg.asLong();
        final int ch = format.charAt(format.length() - 1);
        if (ch == 'i') {
            format = format.substring(0, format.length() - 1) + "d";
        } else if (ch == 'u') {
            format = format.substring(0, format.length() - 1) + "d";
            argument = argument >= 0
                ? argument
                : -Integer.MIN_VALUE + argument;
        } // end if/elsif
        f.format(format, argument);
        return Value.createFromBlock(Block.createString(sb.toString()));
    } // end method 'caml_format_int(CodeRunner, Value, Value)'

    /**
     * Computes the opposite of an int32 value.
     * @param ctxt context
     * @param v int32 value
     * @return <tt>-v</tt>
     */
    @Primitive
    public static Value caml_int32_neg(final CodeRunner ctxt,
                                       final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(-v.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_neg(CodeRunner, Value)'

    /**
     * Computes the sum of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 + v2</tt>
     */
    @Primitive
    public static Value caml_int32_add(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() + v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_add(CodeRunner, Value, Value)'

    /**
     * Computes the difference between two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 - v2</tt>
     */
    @Primitive
    public static Value caml_int32_sub(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() - v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_sub(CodeRunner, Value, Value)'

    /**
     * Computes the product of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 * v2</tt>
     */
    @Primitive
    public static Value caml_int32_mul(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() * v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_mul(CodeRunner, Value, Value)'

    /**
     * Computes the division of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 / v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_int32_div(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        final int divider = v2.asBlock().asInt32();
        if (divider == 0) {
            Fail.raiseZeroDivide();
        } // end if
        b.setInt32(v1.asBlock().asInt32() / divider);
        return Value.createFromBlock(b);
    } // end method 'caml_int32_div(CodeRunner, Value, Value)'

    /**
     * Computes the modulo of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 mod v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_int32_mod(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        final int divider = v2.asBlock().asInt32();
        if (divider == 0) {
            Fail.raiseZeroDivide();
        } // end if
        b.setInt32(v1.asBlock().asInt32() % divider);
        return Value.createFromBlock(b);
    } // end method 'caml_int32_mod(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical and' of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 and v2</tt>
     */
    @Primitive
    public static Value caml_int32_and(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() & v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_and(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical or' of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 or v2</tt>
     */
    @Primitive
    public static Value caml_int32_or(final CodeRunner ctxt,
                                      final Value v1,
                                      final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() | v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_or(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical xor' of two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>v1 xor v2</tt>
     */
    @Primitive
    public static Value caml_int32_xor(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() ^ v2.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_xor(CodeRunner, Value, Value)'

    /**
     * Computes the 'left shift' of an int32 value.
     * @param ctxt context
     * @param v1 int32 value
     * @param v2 long value
     * @return <tt>v1 << v2</tt>
     */
    @Primitive
    public static Value caml_int32_shift_left(final CodeRunner ctxt,
                                              final Value v1,
                                              final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() << v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_shift_left(CodeRunner, Value, Value)'

    /**
     * Computes the 'arithmetic right shift' of an int32 value.
     * @param ctxt context
     * @param v1 int32 value
     * @param v2 long value
     * @return <tt>v1 >> v2</tt>
     */
    @Primitive
    public static Value caml_int32_shift_right(final CodeRunner ctxt,
                                               final Value v1,
                                               final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() >> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_shift_right(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical right shift' of an int32 value.
     * @param ctxt context
     * @param v1 int32 value
     * @param v2 long value
     * @return <tt>v1 >>> v2</tt>
     */
    @Primitive
    public static Value caml_int32_shift_right_unsigned(final CodeRunner ctxt,
                                                        final Value v1,
                                                        final Value v2) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v1.asBlock().asInt32() >>> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_shift_right_unsigned(CodeRunner, Value, Value)'

    /**
     * Converts an int value into an int32 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int32 value
     */
    @Primitive
    public static Value caml_int32_of_int(final CodeRunner ctxt,
                                          final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_of_int(CodeRunner, Value)'

    /**
     * Converts an int32 value into an int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int value
     */
    @Primitive
    public static Value caml_int32_to_int(final CodeRunner ctxt,
                                          final Value v) {
        return Value.createFromLong(v.asBlock().asInt32());
    } // end method 'caml_int32_to_int(CodeRunner, Value)'

    /**
     * Converts a float value into an int32 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int32 value
     */
    @Primitive
    public static Value caml_int32_of_float(final CodeRunner ctxt,
                                            final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32((int) v.asBlock().asDouble());
        return Value.createFromBlock(b);
    } // end method 'caml_int32_of_float(CodeRunner, Value)'

    /**
     * Converts an int32 value into a float value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a float value
     */
    @Primitive
    public static Value caml_int32_to_float(final CodeRunner ctxt,
                                            final Value v) {
        return Value.createFromBlock(Block.createDouble(v.asBlock().asInt32()));
    } // end method 'caml_int32_to_float(CodeRunner, Value)'

    /**
     * Compares two int32 values.
     * @param ctxt context
     * @param v1 first int32 value
     * @param v2 second int32 value
     * @return <tt>-1</tt>, <tt>0</tt>, <tt>1</tt> whether the first value is
     *         less than, equal to or greater than the second one
     */
    @Primitive
    public static Value caml_int32_compare(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final int r1 = v1.asBlock().asInt32();
        final int r2 = v2.asBlock().asInt32();
        final int res;
        if (r1 < r2) {
            return Compare.LESS_VALUE;
        } else if (r1 > r2) {
            return Compare.GREATER_VALUE;
        } else {
            return Compare.EQUAL_VALUE;
        } // end if/elsif/else
    } // end method 'caml_int32_compare(CodeRunner, Value, Value)'

    /**
     * Formats a 32-bit integer, that is converts it to a string.
     * @param ctxt context
     * @param fmt conversion format
     * @param arg integer to convert
     * @return <tt>arg</tt> as a string
     */
    @Primitive
    public static Value caml_int32_format(final CodeRunner ctxt,
                                          final Value fmt,
                                          final Value arg) {
        final StringBuilder sb = new StringBuilder();
        final Formatter f = new Formatter(sb);
        String format = fmt.asBlock().asString();
        long argument = arg.asBlock().asInt32();
        if ((format.length() >= 2)
            && (format.charAt(format.length() - 2) == 'l')) {
            format = format.substring(0, format.length() - 2) + format.substring(format.length() - 1);
        } // end if
        final int ch = format.charAt(format.length() - 1);
        if (ch == 'i') {
            format = format.substring(0, format.length() - 1) + "d";
        } else if (ch == 'u') {
            format = format.substring(0, format.length() - 1) + "d";
            argument = Misc.signedToUnsigned((int) argument);
        } // end if/elsif
        f.format(format, argument);
        return Value.createFromBlock(Block.createString(sb.toString()));
    } // end method 'caml_int32_format(CodeRunner, Value, Value)'

    /**
     * Converts a string value into an int32 value.
     * @param ctxt context
     * @param s string to convert
     * @return the int32 value corresponding to <tt>s</tt>
     * @throws Fail.Exception if a parsing error occurs
     */
    @Primitive
    public static Value caml_int32_of_string(final CodeRunner ctxt,
                                             final Value s)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32((int) parseLong(s.asBlock().asString(), 32));
        return Value.createFromBlock(b);
    } // end method 'caml_int32_of_string(CodeRunner, Value)'

    /**
     * Converts bits of a float value into an int32 value.
     * @param ctxt context
     * @param v float value to convert
     * @return the 32 lowest bits of <tt>v</tt> as an int32
     */
    @Primitive
    public static Value caml_int32_bits_of_float(final CodeRunner ctxt,
                                                 final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32((Float.floatToRawIntBits((float) v.asBlock().asDouble())));
        return Value.createFromBlock(b);
    } // end method 'caml_int32_bits_of_float(CodeRunner, Value)'

    /**
     * Converts an bits of an int32 value into a float value.
     * @param ctxt context
     * @param v int32 value
     * @return a float constructed from the 32 bits of <tt>v</tt>
     */
    @Primitive
    public static Value caml_int32_float_of_bits(final CodeRunner ctxt,
                                                 final Value v) {
        return Value.createFromBlock(Block.createDouble(Float.intBitsToFloat(v.asBlock().asInt32())));
    } // end method 'caml_int32_float_of_bits(CodeRunner, Value)'

    /**
     * Computes the opposite of an int64 value.
     * @param ctxt context
     * @param v int64 value
     * @return <tt>-v</tt>
     */
    @Primitive
    public static Value caml_int64_neg(final CodeRunner ctxt,
                                       final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(-v.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_neg(CodeRunner, Value)'

    /**
     * Computes the sum of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 + v2</tt>
     */
    @Primitive
    public static Value caml_int64_add(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() + v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_add(CodeRunner, Value, Value)'

    /**
     * Computes the difference between two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 - v2</tt>
     */
    @Primitive
    public static Value caml_int64_sub(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() - v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_sub(CodeRunner, Value, Value)'

    /**
     * Computes the product of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 * v2</tt>
     */
    @Primitive
    public static Value caml_int64_mul(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() * v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_mul(CodeRunner, Value, Value)'

    /**
     * Computes the division of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 / v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_int64_div(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        final long divider = v2.asBlock().asInt64();
        if (divider == 0L) {
            Fail.raiseZeroDivide();
        } // end if
        b.setInt64(v1.asBlock().asInt64() / divider);
        return Value.createFromBlock(b);
    } // end method 'caml_int64_div(CodeRunner, Value, Value)'

    /**
     * Computes the modulo of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 mod v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_int64_mod(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        final long divider = v2.asBlock().asInt64();
        if (divider == 0) {
            Fail.raiseZeroDivide();
        } // end if
        b.setInt64(v1.asBlock().asInt64() % divider);
        return Value.createFromBlock(b);
    } // end method 'caml_int64_mod(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical and' of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 and v2</tt>
     */
    @Primitive
    public static Value caml_int64_and(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() & v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_and(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical or' of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 or v2</tt>
     */
    @Primitive
    public static Value caml_int64_or(final CodeRunner ctxt,
                                      final Value v1,
                                      final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() | v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_or(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical xor' of two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>v1 xor v2</tt>
     */
    @Primitive
    public static Value caml_int64_xor(final CodeRunner ctxt,
                                       final Value v1,
                                       final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() ^ v2.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_xor(CodeRunner, Value, Value)'

    /**
     * Computes the 'left shit' of an int64 value.
     * @param ctxt context
     * @param v1 int64 value
     * @param v2 long value
     * @return <tt>v1 << v2</tt>
     */
    @Primitive
    public static Value caml_int64_shift_left(final CodeRunner ctxt,
                                              final Value v1,
                                              final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() << v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_shift_left(CodeRunner, Value, Value)'

    /**
     * Computes the 'arithmetic right shift' of an int64 value.
     * @param ctxt context
     * @param v1 int64 value
     * @param v2 long value
     * @return <tt>v1 >> v2</tt>
     */
    @Primitive
    public static Value caml_int64_shift_right(final CodeRunner ctxt,
                                               final Value v1,
                                               final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() >> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_shift_right(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical right shift' of an int64 value.
     * @param ctxt context
     * @param v1 int64 value
     * @param v2 long value
     * @return <tt>v1 >>> v2</tt>
     */
    @Primitive
    public static Value caml_int64_shift_right_unsigned(final CodeRunner ctxt,
                                                        final Value v1,
                                                        final Value v2) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v1.asBlock().asInt64() >>> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_shift_right_unsigned(CodeRunner, Value, Value)'

    /**
     * Converts an int value into an int64 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int64 value
     */
    @Primitive
    public static Value caml_int64_of_int(final CodeRunner ctxt,
                                          final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_of_int(CodeRunner, Value)'

    /**
     * Converts an int64 value into an int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int value
     */
    @Primitive
    public static Value caml_int64_to_int(final CodeRunner ctxt,
                                          final Value v) {
        return Value.createFromLong((int) v.asBlock().asInt64());
    } // end method 'caml_int64_to_int(CodeRunner, Value)'

    /**
     * Converts a float value into an int64 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int64 value
     */
    @Primitive
    public static Value caml_int64_of_float(final CodeRunner ctxt,
                                            final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64((long) v.asBlock().asDouble());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_of_float(CodeRunner, Value)'

    /**
     * Converts an int64 value into a float value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a float value
     */
    @Primitive
    public static Value caml_int64_to_float(final CodeRunner ctxt,
                                            final Value v) {
        return Value.createFromBlock(Block.createDouble(v.asBlock().asInt64()));
    } // end method 'caml_int64_to_float(CodeRunner, Value)'

    /**
     * Converts an int32 value into an int64 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int64 value
     */
    @Primitive
    public static Value caml_int64_of_int32(final CodeRunner ctxt,
                                            final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_of_int32(CodeRunner, Value)'

    /**
     * Converts an int64 value into an int32 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int32 value
     */
    @Primitive
    public static Value caml_int64_to_int32(final CodeRunner ctxt,
                                            final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32((int) v.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_to_int32(CodeRunner, Value)'

    /**
     * Converts a native int value into an int64 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int64 value
     */
    @Primitive
    public static Value caml_int64_of_nativeint(final CodeRunner ctxt,
                                                final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(v.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_of_nativeint(CodeRunner, Value)'

    /**
     * Converts an int64 value into a native int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a native int value
     */
    @Primitive
    public static Value caml_int64_to_nativeint(final CodeRunner ctxt,
                                                final Value v) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt((int) v.asBlock().asInt64());
        return Value.createFromBlock(b);
    } // end method 'caml_int64_to_nativeint(CodeRunner, Value)'

    /**
     * Compares two int64 values.
     * @param ctxt context
     * @param v1 first int64 value
     * @param v2 second int64 value
     * @return <tt>-1</tt>, <tt>0</tt>, <tt>1</tt> whether the first value is
     *         less than, equal to or greater than the second one
     */
    @Primitive
    public static Value caml_int64_compare(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final long r1 = v1.asBlock().asInt64();
        final long r2 = v2.asBlock().asInt64();
        final int res;
        if (r1 < r2) {
            return  Compare.LESS_VALUE;
        } else if (r1 > r2) {
            return Compare.GREATER_VALUE;
        } else {
            return Compare.EQUAL_VALUE;
        } // end if/elsif/else
    } // end method 'caml_int64_compare(CodeRunner, Value, Value)'

    /**
     * Formats a 64-bit integer, that is converts it to a string.
     * @param ctxt context
     * @param fmt conversion format
     * @param arg integer to convert
     * @return <tt>arg</tt> as a string
     */
    @Primitive
    public static Value caml_int64_format(final CodeRunner ctxt,
                                          final Value fmt,
                                          final Value arg) {
        final StringBuilder sb = new StringBuilder();
        final Formatter f = new Formatter(sb);
        String format = fmt.asBlock().asString();
        final long argument = arg.asBlock().asInt64();
        if ((format.length() >= 2)
            && (format.charAt(format.length() - 2) == 'L')) {
            format = format.substring(0, format.length() - 2) + format.substring(format.length() - 1);
        } // end if
        if (format.charAt(format.length() - 1) == 'i') {
            format = format.substring(0, format.length() - 1) + "d";
        } else if (format.charAt(format.length() - 1) == 'u') {
            format = format.substring(0, format.length() - 1) + "d";
            if (argument < 0) {
                final String res =
                    BigInteger.ONE.shiftLeft(64).add(BigInteger.valueOf(argument)).toString();
                return Value.createFromBlock(Block.createString(res));
            } // end if
        } // end if/elsif
        f.format(format, argument);
        return Value.createFromBlock(Block.createString(sb.toString()));
    } // end method 'caml_int64_format(CodeRunner, Value, Value)'

    /**
     * Converts a string value into an int64 value.
     * @param ctxt context
     * @param s string to convert
     * @return the int64 value corresponding to <tt>s</tt>
     * @throws Fail.Exception if a parsing error occurs
     */
    @Primitive
    public static Value caml_int64_of_string(final CodeRunner ctxt,
                                             final Value s)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(parseLong(s.asBlock().asString(), 64));
        return Value.createFromBlock(b);
    } // end method 'caml_int64_of_string(CodeRunner, Value)'

    /**
     * Converts bits of a float value into an int64 value.
     * @param ctxt context
     * @param v float value to convert
     * @return the 64 bits of <tt>v</tt> as an int364
     */
    @Primitive
    public static Value caml_int64_bits_of_float(final CodeRunner ctxt,
                                                 final Value v) {
        final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                           Custom.INT_64_OPS);
        b.setInt64(Double.doubleToRawLongBits(v.asBlock().asDouble()));
        return Value.createFromBlock(b);
    } // end method 'caml_int64_bits_of_float(CodeRunner, Value)'

    /**
     * Converts bits of an int64 value into a float value.
     * @param ctxt context
     * @param val int64 value
     * @return a float constructed from the 64 bits of <tt>v</tt>
     */
    @Primitive
    public static Value caml_int64_float_of_bits(final CodeRunner ctxt,
                                                 final Value val) {
        return Value.createFromBlock(Block.createDouble(Double.longBitsToDouble(val.asBlock().asInt64())));
    } // end method 'caml_int64_float_of_bits(CodeRunner, Value)'

    /**
     * Computes the opposite of a native int value.
     * @param ctxt context
     * @param v native int value
     * @return <tt>-v</tt>
     */
    @Primitive
    public static Value caml_nativeint_neg(final CodeRunner ctxt,
                                           final Value v) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(-v.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_neg(CodeRunner, Value)'

    /**
     * Computes the sum of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 + v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_add(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() + v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_add(CodeRunner, Value, Value)'

    /**
     * Computes the difference between two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 - v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_sub(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() - v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_sub(CodeRunner, Value, Value)'

    /**
     * Computes the product of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 * v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_mul(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() * v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_mul(CodeRunner, Value, Value)'

    /**
     * Computes the division of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 / v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_nativeint_div(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        final int divider = v2.asBlock().asNativeInt();
        if (divider == 0) {
            Fail.raiseZeroDivide();
        } // end if
        b.setNativeInt(v1.asBlock().asNativeInt() / divider);
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_div(CodeRunner, Value, Value)'

    /**
     * Computes the modulo of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 mod v2</tt>
     * @throws Fail.Exception raises <i>Zero_divide</i> if v2 equals zero
     */
    @Primitive
    public static Value caml_nativeint_mod(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        final int divider = v2.asBlock().asNativeInt();
        if (divider == 0) {
            Fail.raiseZeroDivide();
        } // end if
        b.setNativeInt(v1.asBlock().asNativeInt() % divider);
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_mod(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical and' of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 and v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_and(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() & v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_and(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical or' of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 or v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_or(final CodeRunner ctxt,
                                          final Value v1,
                                          final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() | v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_or(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical xor' of two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>v1 xor v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_xor(final CodeRunner ctxt,
                                           final Value v1,
                                           final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() ^ v2.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_xor(CodeRunner, Value, Value)'

    /**
     * Computes the 'left shit' of a native int value.
     * @param ctxt context
     * @param v1 native int value
     * @param v2 long value
     * @return <tt>v1 << v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_shift_left(final CodeRunner ctxt,
                                                  final Value v1,
                                                  final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() << v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_shift_left(CodeRunner, Value, Value)'

    /**
     * Computes the 'arithmetic right shift' of a native int value.
     * @param ctxt context
     * @param v1 native int value
     * @param v2 long value
     * @return <tt>v1 >> v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_shift_right(final CodeRunner ctxt,
                                                   final Value v1,
                                                   final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() >> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_shift_right(CodeRunner, Value, Value)'

    /**
     * Computes the 'logical right shift' of a native int value.
     * @param ctxt context
     * @param v1 native int value
     * @param v2 long value
     * @return <tt>v1 >>> v2</tt>
     */
    @Primitive
    public static Value caml_nativeint_shift_right_unsigned(final CodeRunner ctxt,
                                                            final Value v1,
                                                            final Value v2) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v1.asBlock().asNativeInt() >>> v2.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_shift_right_unsigned(CodeRunner, Value, Value)'

    /**
     * Converts an int value into a native int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a native int value
     */
    @Primitive
    public static Value caml_nativeint_of_int(final CodeRunner ctxt,
                                              final Value v) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v.asLong());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_of_int(CodeRunner, Value)'

    /**
     * Converts a native int value into an int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int value
     */
    @Primitive
    public static Value caml_nativeint_to_int(final CodeRunner ctxt,
                                              final Value v) {
        return Value.createFromLong(v.asBlock().asNativeInt());
    } // end method 'caml_nativeint_to_int(CodeRunner, Value)'

    /**
     * Converts a float value into a native int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a native int value
     */
    @Primitive
    public static Value caml_nativeint_of_float(final CodeRunner ctxt,
                                                final Value v) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt((int) v.asBlock().asDouble());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_of_float(CodeRunner, Value)'

    /**
     * Converts a native int value into a float value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a float value
     */
    @Primitive
    public static Value caml_nativeint_to_float(final CodeRunner ctxt,
                                                final Value v) {
        return Value.createFromBlock(Block.createDouble(v.asBlock().asNativeInt()));
    } // end method 'caml_nativeint_to_float(CodeRunner, Value)'

    /**
     * Converts an int32 value into a native int value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as a native int value
     */
    @Primitive
    public static Value caml_nativeint_of_int32(final CodeRunner ctxt,
                                                final Value v) {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt(v.asBlock().asInt32());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_of_int32(CodeRunner, Value)'

    /**
     * Converts a native int value into an int32 value.
     * @param ctxt context
     * @param v value to convert
     * @return <tt>v</tt> as an int32 value
     */
    @Primitive
    public static Value caml_nativeint_to_int32(final CodeRunner ctxt,
                                                final Value v) {
        final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                           Custom.INT_32_OPS);
        b.setInt32(v.asBlock().asNativeInt());
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_to_int32(CodeRunner, Value)'

    /**
     * Compares two native int values.
     * @param ctxt context
     * @param v1 first native int value
     * @param v2 second native int value
     * @return <tt>-1</tt>, <tt>0</tt>, <tt>1</tt> whether the first value is
     *         less than, equal to or greater than the second one
     */
    @Primitive
    public static Value caml_nativeint_compare(final CodeRunner ctxt,
                                               final Value v1,
                                               final Value v2) {
        final int r1 = v1.asBlock().asNativeInt();
        final int r2 = v2.asBlock().asNativeInt();
        final int res;
        if (r1 < r2) {
            return Compare.LESS_VALUE;
        } else if (r1 > r2) {
            return Compare.GREATER_VALUE;
        } else {
            return Compare.EQUAL_VALUE;
        } // end if/elsif/else
    } // end method 'caml_nativeint_compare(CodeRunner, Value, Value)'

    /**
     * Formats a native integer, that is converts it to a string.
     * @param ctxt context
     * @param fmt conversion format
     * @param arg integer to convert
     * @return <tt>arg</tt> as a string
     */
    @Primitive
    public static Value caml_nativeint_format(final CodeRunner ctxt,
                                              final Value fmt,
                                              final Value arg) {
        final StringBuilder sb = new StringBuilder();
        final Formatter f = new Formatter(sb);
        String format = fmt.asBlock().asString();
        long argument = arg.asBlock().asNativeInt();
        if ((format.length() >= 2)
            && (format.charAt(format.length() - 2) == 'n')) {
            format = format.substring(0, format.length() - 2) + format.substring(format.length() - 1);
        } // end if
        if (format.charAt(format.length() - 1) == 'i') {
            format = format.substring(0, format.length() - 1) + "d";
        } else if (format.charAt(format.length() - 1) == 'u') {
            format = format.substring(0, format.length() - 1) + "d";
            argument = Misc.signedToUnsigned((int) argument);
        } // end if/elsif
        f.format(format, argument);
        return Value.createFromBlock(Block.createString(sb.toString()));
    } // end method 'caml_nativeint_format(CodeRunner, Value, Value)'

    /**
     * Converts a string value into a native int value.
     * @param ctxt context
     * @param s string to convert
     * @return the native int value corresponding to <tt>s</tt>
     * @throws Fail.Exception if a parsing error occurs
     */
    @Primitive
    public static Value caml_nativeint_of_string(final CodeRunner ctxt,
                                                 final Value s)
        throws Fail.Exception {
        final Block b = Block.createCustom(Custom.INT_NAT_SIZE,
                                           Custom.INT_NAT_OPS);
        b.setNativeInt((int) parseLong(s.asBlock().asString(), 32));
        return Value.createFromBlock(b);
    } // end method 'caml_nativeint_of_string(CodeRunner, Value)'

} // end class 'Ints'
